/**
 * 
 */
package kr.co.insoft.auth.security;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper;
import org.springframework.security.web.context.SecurityContextRepository;

/**
 * @author IN-SOFT Inc. GoodwillDD(sylee@in-soft.co.kr)
 * 
 */
public class CookieSecurityContextRepository implements
		SecurityContextRepository, InitializingBean {

	private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();

	final SecurityCookieService securityCookieService;

	public CookieSecurityContextRepository(
			SecurityCookieService securityCookieService) {
		this.securityCookieService = securityCookieService;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.security.web.context.SecurityContextRepository#
	 * containsContext(javax.servlet.http.HttpServletRequest)
	 */
	@Override
	public boolean containsContext(HttpServletRequest request) {
		return securityCookieService.containsSecurityCookie(request);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.security.web.context.SecurityContextRepository#
	 * loadContext
	 * (org.springframework.security.web.context.HttpRequestResponseHolder)
	 */
	@Override
	public SecurityContext loadContext(
			HttpRequestResponseHolder requestResponseHolder) {
		HttpServletRequest request = requestResponseHolder.getRequest();

		SecurityContext context = SecurityContextHolder.createEmptyContext();

		SaveToCookieResponseWrapper response = new SaveToCookieResponseWrapper(
				requestResponseHolder.getResponse(), false, context.hashCode());
		requestResponseHolder.setResponse(response);

		if (request.getCookies() != null) {
			Cookie securityCookie = securityCookieService
					.getSecurityCookieFrom(request);
			if (securityCookie != null) {
				Authentication authentication = securityCookieService
						.getAuthenticationFrom(securityCookie);
				if (authentication == null) {
					Cookie cookie = securityCookieService.createLogoutCookie();
					requestResponseHolder.getResponse().addCookie(cookie);
				}

				context.setAuthentication(authentication);
				return context;
			}
		}

		return context;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.security.web.context.SecurityContextRepository#
	 * saveContext(org.springframework.security.core.context.SecurityContext,
	 * javax.servlet.http.HttpServletRequest,
	 * javax.servlet.http.HttpServletResponse)
	 */
	@Override
	public void saveContext(SecurityContext context,
			HttpServletRequest request, HttpServletResponse response) {
		SaveToCookieResponseWrapper responseWrapper = (SaveToCookieResponseWrapper) response;

		if (!responseWrapper.isContextSaved()) {
			responseWrapper.saveContext(context);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
	}

	final class SaveToCookieResponseWrapper extends
			SaveContextOnUpdateOrErrorResponseWrapper {
		private int contextHashBeforeChainExecution;

		public SaveToCookieResponseWrapper(HttpServletResponse response,
				boolean disableUrlRewriting, int contextHashBeforeChainExecution) {
			super(response, disableUrlRewriting);
			this.contextHashBeforeChainExecution = contextHashBeforeChainExecution;
		}

		@Override
		protected void saveContext(SecurityContext context) {
			final Authentication authentication = context.getAuthentication();

			// See SEC-776
			if (authentication == null
					|| authenticationTrustResolver.isAnonymous(authentication)) {
				return;
			}

			// If HttpSession exists, store current SecurityContextHolder
			// contents but only if the SecurityContext has actually changed
			// (see JIRA SEC-37)
			// We also check that the session contains the context, in case a
			// new session has been created (SEC-1561)
			if (context.hashCode() != contextHashBeforeChainExecution) {
			}

			Cookie securityCookie = securityCookieService
					.createSecurityCookie(context.getAuthentication());
			if (securityCookie != null) {
				if (!this.isContextSaved()) {
					addCookie(securityCookie);
				}
			} else {
				addCookie(securityCookieService.createLogoutCookie());
			}
		}
	}

}
